#include "cmonthview.h"

#include "ccalmanager.h"

#include <buola/image/cimage.h>
#include <buola/image/cgc.h>
#include <buola/image/cfont.h>
#include <buola/gui/cmenu.h>
#include <buola/gui/cconfig.h>
#include <buola/image/cpattern.h>
#include <buola/datetime/cdatetime.h>
#include <buola/widgets/cscrollbar.h>
#include <buola/iterator/indirect.h>

namespace buola { namespace bcal {

CMonthView::CMonthView(CCalManager *pManager)
    :    mManager(pManager),
        mValid(VALID_NONE)
{
    mManager->sCalendarAdded.Connect(&CMonthView::OnCalendarAdded,this,_1);

    SetBackColor(img::CColor(1,1,1));

    SetPointer(gui::CPointer::STOCK_DEFAULT);

    mFont=img::CFont::GetStock(L"menu");
}

CMonthView::~CMonthView()
{
}

///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
////////////////////////// Interface //////////////////////////
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////

void CMonthView::SetDate(const CDate &pDate)
{
    mDate=pDate;
    Invalidate(VALID_ENTRIES|VALID_SHAPE|VALID_POSITIONS);
    sChangeTitle(mDate.GetMonthYearText());
    Refresh();
}

void CMonthView::SetToday()
{
    CDate lDate(CCalendarClock::now());
    SetDate(lDate);
}

void CMonthView::Previous()
{
    SetDate(mDate.PreviousMonth());
}

void CMonthView::Next()
{
    SetDate(mDate.NextMonth());
}

void CMonthView::CalcEntries()
{
    mRows=CDate::WeeksInMonth(mDate.Year(),mDate.Month());
    CDate lDate(mDate.Year(),mDate.Month(),1);
    mFirstDate=lDate-(lDate.WeekDay()-1);
    
    mDays.clear();

    for(int i=0;i<7*mRows;i++)
    {
        construct_back(mDays);
    }
    
    lDate=mFirstDate;
    
    for(auto lDay=indirect_begin(mDays);lDay!=indirect_end(mDays);++lDay)
    {
        for(auto lC=mManager->GetCalendars().begin();lC!=mManager->GetCalendars().end();++lC)
        {
            std::vector<CCalendarEvent*> lEvents;
            
            (*lC)->GetEventsBetween(lEvents,lDate.GetStartTime(),lDate.GetEndTime());

            img::CColor lColor=(*lC)->GetColor();
            
            for(int i=0;i<lEvents.size();i++)
            {
                SEntry &lEntry=construct_back(lDay->mEntries);
                
                lEntry.mEvent=lEvents[i];
                lEntry.mBallColor=lColor;
            }
        }
        
        lDate++;
    }
    
    mValid|=VALID_ENTRIES;
}

void CMonthView::CalcShape()
{
    mMonthsRect={0,mFont->GetHeight(),Size().x,Size().y};

    int n=0;

    for(int i=0;i<mRows;i++)
    {
        double lY=mMonthsRect.t+floor(i*mMonthsRect.h()/mRows);
        double lYNext=mMonthsRect.t+floor((i+1)*mMonthsRect.h()/mRows);

        for(int j=0;j<7;j++)
        {
            mDays[n]->mRect.t=lY;
            mDays[n]->mRect.b=lYNext;
            mDays[n]->mRect.l=mMonthsRect.l+floor(j*mMonthsRect.w()/7);
            mDays[n]->mRect.r=mMonthsRect.l+floor((j+1)*mMonthsRect.w()/7);
            n++;
        }
    }

    mValid|=VALID_SHAPE;
}

void CMonthView::CalcPositions()
{
    for(auto lDay=indirect_begin(mDays);lDay!=indirect_end(mDays);++lDay)
    {
        double lY=lDay->mRect.t+10;

        for(auto lEntry=indirect_begin(lDay->mEntries);lEntry!=indirect_end(lDay->mEntries);++lEntry)
        {
            lEntry->mPos.x=lDay->mRect.l+3;
            lEntry->mPos.y=lY;
            lY+=12;
        }
    }

    mValid|=VALID_POSITIONS;
}

void CMonthView::MakeValid(EValid pWhat)
{
    if((pWhat&VALID_ENTRIES)&&!(mValid&VALID_ENTRIES))
        CalcEntries();
    if((pWhat&VALID_SHAPE)&&!(mValid&VALID_SHAPE))
        CalcShape();
    if((pWhat&VALID_POSITIONS)&&!(mValid&VALID_POSITIONS))
        CalcPositions();
}

///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
/////////////////////// Event handlers ////////////////////////
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////

void CMonthView::OnPreExpose()
{
    MakeValid(VALID_ENTRIES|VALID_SHAPE|VALID_POSITIONS);
}
    
static const wchar_t *sWeekDays[]=
{
    L"Monday",
    L"Tuesday",
    L"Wednesday",
    L"Thursday",
    L"Friday",
    L"Saturday",
    L"Sunday"
};

void CMonthView::OnExpose(const gui::CExposeEvent &pE)
{
    img::CGC *lGC=pE.mGC;

    CDate lToday(CCalendarClock::now());

    CDate lDate(mFirstDate);

    lGC->SetFont(mFont);

    for(auto lDay=indirect_begin(mDays);lDay!=indirect_end(mDays);++lDay)
    {
        if(lDate==lToday)
        {
            lGC->SetSource(img::CColor(0.88,0.92,1.0));
            lGC->Path().Rect(lDay->mRect.TopLeft(),lDay->mRect.Size());
            lGC->FillPath();
        }
        else if(lDate==mDate)
        {
            lGC->SetSource(img::CColor(0.88,0.88,0.88));
            lGC->Path().Rect(lDay->mRect.TopLeft(),lDay->mRect.Size());
            lGC->FillPath();
        }
            
        if(lDate.Month()==mDate.Month())
            lGC->SetSource(img::CColor(0.2,0.2,0.2));
        else
            lGC->SetSource(img::CColor(0.8,0.8,0.8));

        lGC->DrawText(CPoint_d(lDay->mRect.r-2,lDay->mRect.t+2),to_wstring(lDate.Day()),img::ETextPos::RIGHT);

        for(auto lEntry=indirect_begin(lDay->mEntries);lEntry!=indirect_end(lDay->mEntries);++lEntry)
        {
            lGC->SetSource(lEntry->mBallColor);
            lGC->Path().Arc(CPoint_d(lEntry->mPos.x+3,lEntry->mPos.y+5),3);
            lGC->FillPath();
            lGC->SetSource(img::CColor(0.2,0.2,0.2));
            lGC->DrawText(lEntry->mPos+CPoint_d(10,0),lEntry->mEvent->GetSummary());
        }
        
        ++lDate;
    }

    //draw lines
    lGC->SetSource(img::CColor(0.8,0.8,0.8));
    lGC->SetLineWidth(1.0);

    for(int i=0;i<mRows;i++)
    {
        double lY=mMonthsRect.t+floor(i*mMonthsRect.h()/mRows);

        lGC->Path().MoveTo(CPoint_d(mMonthsRect.l,lY+0.5));
        lGC->Path().LineTo(CPoint_d(mMonthsRect.r,lY+0.5));
        lGC->StrokePath();
    }
    
    lGC->Path().MoveTo(CPoint_d(mMonthsRect.l,mMonthsRect.b-0.5));
    lGC->Path().LineTo(CPoint_d(mMonthsRect.r,mMonthsRect.b-0.5));
    lGC->StrokePath();

    for(int j=0;j<7;j++)
    {
        double lX=mMonthsRect.l+floor(j*mMonthsRect.w()/7);
        lGC->Path().MoveTo(CPoint_d(lX+0.5,mMonthsRect.t));
        lGC->Path().LineTo(CPoint_d(lX+0.5,mMonthsRect.b));
        lGC->StrokePath();
    }

    lGC->Path().MoveTo(CPoint_d(mMonthsRect.r-0.5,mMonthsRect.t));
    lGC->Path().LineTo(CPoint_d(mMonthsRect.r-0.5,mMonthsRect.b));
    lGC->StrokePath();

    lGC->SetSource(img::CColor(0.2,0.2,0.2));
    lGC->SetStockFont(L"small");

    for(int j=0;j<7;j++)
    {
        double lX=mMonthsRect.l+floor((j+0.5)*mMonthsRect.w()/7);
        lGC->DrawText(CPoint_d(lX,mMonthsRect.t),sWeekDays[j],img::ETextPos::HCENTER|img::ETextPos::BOTTOM);
    }
}    

void CMonthView::OnButtonDown(const gui::CMouseEvent &pE)
{
}

void CMonthView::OnButtonUp(const gui::CMouseEvent &pE)
{
}

void CMonthView::OnMouseMove(const gui::CMouseEvent &pE)
{
}

void CMonthView::OnSize(const gui::CGeometryEvent &pE)
{
    Invalidate(VALID_SHAPE|VALID_POSITIONS);
}

void CMonthView::OnStyleChanged(TStyle)
{
//    Refresh();
}

void CMonthView::OnCalendarAdded(CCalendar *pCalendar)
{
    Invalidate(VALID_ENTRIES|VALID_SHAPE|VALID_POSITIONS);
    Refresh();
}

void CMonthView::OnCalendarRemoved(CCalendar *pCalendar)
{
    Invalidate(VALID_ENTRIES|VALID_SHAPE|VALID_POSITIONS);
    Refresh();
}

/*namespace buola*/ } /*namespace bcal*/ }
